﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO.Ports;
using System.Threading;

//two work mode of GSMPort:
//      1: thread mode,in which case the GSMPort enter into a run loop to receive msg
//           thread mode can support request/response also
//           so to distinguish we use 2 handler, partial handler and whole msg handler
//           whole msg should end with OK or ERROR
//      2: command mode, the GSMPort works in request/response mode
namespace call2sms
{
    class GSMPort
    {
        public enum GSMPortWorkMode
        {
           
            RequestResponseMode,
            ThreadLoopMode,
        };

        public delegate void PartialMsgEventHandler(string msg);
        public delegate void WholeMsgEventHandler(string msg);


        public event PartialMsgEventHandler PartialMsgReceived;
        public event WholeMsgEventHandler WholeMsgReceived;


        private SerialPort port;
        private AutoResetEvent receiveNow;
        private string ComId;

        private string Msg;

        private bool running = false;
        private AutoResetEvent msgReady = new AutoResetEvent(false);


        public bool IsRunning
        {
            get { return running; }
        }

        public GSMPortWorkMode WorkMode
        {
            get;
            set;
        }

         public GSMPort(string com)
        {
            ComId = com;
            receiveNow = new AutoResetEvent(false);
        }

         ~GSMPort()
        {
            port.Close();
            port.DataReceived -= new SerialDataReceivedEventHandler(port_DataReceived);
            receiveNow.Close();
        }

         public bool IsOpen()
         {
             return port.IsOpen;
         }

         public SerialPort Init()
         {
             port = new SerialPort();
             port.PortName = ComId;
             port.BaudRate = 9600;
             port.DataBits = 8;
             port.StopBits = StopBits.One;
             port.Parity = Parity.None;
             port.ReadTimeout = 300;
             port.WriteTimeout = 300;
             port.Encoding = Encoding.GetEncoding("iso-8859-1");
             port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
             port.Open();
             port.DtrEnable = true;
             port.RtsEnable = true;
             return port;
         }

         public void Start()
         {
             if (WorkMode == GSMPortWorkMode.ThreadLoopMode)
             {
                 Thread thread = new Thread(new ThreadStart(this.RunLoop));
                 thread.Start();
             }
         }


         public void Stop()
         {
             if (WorkMode == GSMPortWorkMode.ThreadLoopMode)
             {
                 running = false;
             }

             port.Close();
         }

         private void RunLoop()
         {
            
             string msg = String.Empty;
             running = true;
             while (running)
             {
                 string partial = ReadAny(300);
                 if (partial != string.Empty)
                 {
                     PartialMsgReceived.Invoke(partial);
                 }

                 msg += partial;
                 if (msg.EndsWith("\r\nOK\r\n") || msg.EndsWith("\r\nERROR\r\n"))
                 {
                     if(WholeMsgReceived != null) WholeMsgReceived.Invoke(msg);
                     msg = String.Empty;
                 }
             }
         }


         public void Close()
         {
             receiveNow.Set();
             port.Close();
             port.DataReceived -= new SerialDataReceivedEventHandler(port_DataReceived);
         }

         void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
         {
             if (e.EventType == SerialData.Chars)
                 receiveNow.Set();
         }


         private string ReadAny(int timeout)
         {
             string buffer = string.Empty;
             if (receiveNow.WaitOne(timeout, false))
             {
                 string t = port.ReadExisting();
                 buffer += t;
             }
             return buffer;
         }


        public string ReadResponse(int timeout, bool waitEndTag)
         {
             string buffer = string.Empty;
             do
             {
                 if (receiveNow.WaitOne(timeout, false))
                 {
                     string t = port.ReadExisting();
                     buffer += t;
                 }
                 else
                 {
                     if (buffer.Length > 0)
                     {
                         Debug.Write("Read timeout\n");
                         throw new ApplicationException("Response received is incomplete.");
                     }
                     else
                         throw new ApplicationException("No data received from phone.");
                 }

                 if (!waitEndTag) break;
             }
             while (!buffer.EndsWith("\r\nOK\r\n") && !buffer.EndsWith("\r\nERROR\r\n"));
             //Debug.Write("------------" + buffer);
             return buffer;
         }

         /*public string ExecCommand(string command, int responseTimeout, string errorMessage)
         {
             try
             {
                 port.DiscardOutBuffer();
                 port.DiscardInBuffer();
                 receiveNow.Reset();
                 port.Write(command + "\r");

                 string input = ReadResponse(responseTimeout, true);
                 if (input.Length == 0)
                     //if ((input.Length == 0) || (!input.EndsWith("\r\nOK\r\n")))
                     throw new ApplicationException("No success message was received.");
                 return input;
             }
             catch (Exception ex)
             {
                 //MessageBox.Show(ex.ToString());
                 return "exception:\r\n" + ex.ToString();
                 //  throw new ApplicationException(errorMessage, ex);
             }
         }*/

         private void onWholeMsg(string msg)
         {
             Msg = msg;
             msgReady.Set();
         }
         public string ExecCommand(string command, int responseTimeout, string errorMessage)
         {
             string outstr;

             Debug.Write("----------------\nexec cmd: " + command+"\n");
             if (WorkMode == GSMPortWorkMode.ThreadLoopMode)
             {
                 msgReady.Reset();
                 WholeMsgReceived += onWholeMsg;

                 port.Write(command + "\r");

                 msgReady.WaitOne(responseTimeout);
                 WholeMsgReceived -= onWholeMsg;

                 outstr = Msg;
                 
             }
             else
             {
                 receiveNow.Reset();
                 port.Write(command + "\r");

                 outstr = ReadResponse(responseTimeout, true);
                 
             }
             Debug.Write("result:" + outstr);
             return outstr;
         }
    }
}
